package org.zjvis.datascience.web.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import java.util.List;
import javax.servlet.http.HttpServletRequest;

import javax.validation.Valid;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.zjvis.datascience.common.annotation.ProjectAuth;
import org.zjvis.datascience.common.annotation.ProjectRoleAuth;
import org.zjvis.datascience.common.dto.TaskDTO;
import org.zjvis.datascience.common.dto.TaskInstanceDTO;
import org.zjvis.datascience.common.enums.TaskTypeEnum;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.model.ApiResult;
import org.zjvis.datascience.common.model.ApiResultCode;
import org.zjvis.datascience.common.model.stat.ColumnAction;
import org.zjvis.datascience.common.model.stat.ColumnConstant;
import org.zjvis.datascience.common.model.stat.ColumnStat;
import org.zjvis.datascience.common.util.CollectionUtil;
import org.zjvis.datascience.common.util.JwtUtil;
import org.zjvis.datascience.common.util.Page;
import org.zjvis.datascience.common.vo.PercentageDataVO;
import org.zjvis.datascience.common.vo.TaskInstanceVO;
import org.zjvis.datascience.common.vo.column.ColumnQueryVO;
import org.zjvis.datascience.common.vo.column.MultiColumnQueryVO;
import org.zjvis.datascience.service.SemanticService;
import org.zjvis.datascience.service.TColumnService;
import org.zjvis.datascience.service.TaskService;

/**
 * @description 字段渲染 Controller
 * @date 2021-12-22
 */
@Api(tags = "tcolumn")
@RestController
@RequestMapping("/tcolumn")
public class TColumnController {

    @Autowired
    private TColumnService tcolumnService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private SemanticService semanticService;

    private final static Logger logger = LoggerFactory.getLogger(TColumnController.class);


    @PostMapping(value = "/queryStat")
    @ResponseBody
    @ApiOperation(value = "字段统计", notes = "字段统计")
    public ApiResult<ColumnStat> queryStat(@RequestBody @Valid ColumnQueryVO vo) {
        //type必传
        if (!tcolumnService.checkTableIfExits(vo)) {
            return ApiResult.valueOf(ApiResultCode.SUCCESS);
        }
        return ApiResult.valueOf(tcolumnService.queryStat(vo));
    }

    @PostMapping(value = "/queryValuesByPage")
    @ResponseBody
    @ApiOperation(value = "分页查询字段值", notes = "分页查询字段值")
    public ApiResult<Page<String>> queryValuesByPage(@RequestBody ColumnQueryVO vo) {
        if (vo == null || StringUtils.isEmpty(vo.getTable()) || StringUtils.isEmpty(vo.getName())) {
            logger.warn("API /tcolumn/queryValuesByPage failed, since {}",
                    ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }

        Page<String> values = tcolumnService.queryValuesByPage(vo);
        return ApiResult.valueOf(values);
    }

    @PostMapping(value = "/queryMinMax")
    @ResponseBody
    @ApiOperation(value = "查询最大最小值", notes = "查询最大最小值")
    public ApiResult<JSONObject> queryMinMax(HttpServletRequest r, @RequestBody ColumnQueryVO vo) {
        if (StringUtils.isEmpty(vo.getTable()) || StringUtils.isEmpty(vo.getName())) {
            logger.warn("API /tcolumn/queryMinMax failed, since {}",
                    ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        JSONObject result = tcolumnService.queryMinMax(vo);
        return ApiResult.valueOf(result);
    }

    @PostMapping(value = "/queryDetail")
    @ResponseBody
    @ApiOperation(value = "字段详情", notes = "字段详情")
    public ApiResult<Object> queryDetail(HttpServletRequest r, @RequestBody @Valid ColumnQueryVO vo) {
        if (!tcolumnService.checkTableIfExits(vo)) {
            return ApiResult.valueOf(ApiResultCode.TABLE_EMPTY);
        }
        //TODO 跟前端对接，看能不能去掉
        if (ColumnConstant.MODE_TABLE.equals(vo.getMode())) {
            JSONArray filter = vo.getFilter();
            if (CollectionUtil.isNotEmpty(filter)) {
                JSONArray arr = new JSONArray();
                for (int i = 0; i < filter.size(); i++) {
                    JSONArray item = new JSONArray();
                    item.add(filter.get(i));
                    arr.add(item);
                }
                vo.setFilter(arr);
            }
        }
        try {
            JSONObject ret = tcolumnService.queryDetail(vo);
            JSONObject modelParams = tcolumnService.getModelParamsForTask(vo.getTaskId());
            if (modelParams != null) {
                ret.put("modelParams", modelParams);
            }
            return ApiResult.valueOf(ret);
        }catch (DataScienceException e){
            return ApiResult.valueOf(ApiResultCode.LOSE_CONNECTION, e.getMessage());
        }
    }

    @PostMapping(value = "/cleanData")
    @ResponseBody
    @ApiOperation(value = "删除重复数据", notes = "删除重复数据")
    public ApiResult<JSONObject> cleanData(HttpServletRequest r, @RequestBody ColumnQueryVO vo) {
        if (vo == null || StringUtils.isEmpty(vo.getTable())) {
            logger.warn("API /tcolumn/cleanData failed, since {}",
                    ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        if (!tcolumnService.checkTableIfExits(vo)) {
            return ApiResult.valueOf(ApiResultCode.SUCCESS);
        }
        JSONObject ret = tcolumnService.cleanData(vo);
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/recommendSeparator")
    @ResponseBody
    @ApiOperation(value = "推荐分隔符", notes = "推荐分隔符")
    public ApiResult<JSONObject> recommendSeparator(@RequestBody ColumnQueryVO vo) {
        if (vo == null || StringUtils.isEmpty(vo.getTable())) {
            logger.warn("API /tcolumn/recommendSeparator failed, since {}",
                    ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        if (!tcolumnService.checkTableIfExits(vo)) {
            return ApiResult.valueOf(ApiResultCode.SUCCESS);
        }
        JSONObject ret = tcolumnService.recommendSeparator(vo);
        return ApiResult.valueOf(ret);
    }


    @PostMapping(value = "/queryAction")
    @ResponseBody
    @ApiOperation(value = "action列表", notes = "action列表")
    public ApiResult<List<ColumnAction>> queryAction(@RequestBody TaskInstanceVO vo) {
        if (null == vo.getTaskId()) {
            logger.warn("API /tcolumn/queryAction failed, since {}", "taskId is empty");
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        List<ColumnAction> taskInstanceVOS = tcolumnService.queryAction(vo.getTaskId());
        return ApiResult.valueOf(taskInstanceVOS);
    }

    @PostMapping(value = "/queryActionById")
    @ResponseBody
    @ApiOperation(value = "根据actionId查询", notes = "查询单个action")
    public ApiResult<ColumnAction> queryActionById(@RequestBody TaskInstanceVO vo) {
        if (null == vo.getId()) {
            logger.warn("API /tcolumn/queryActionById failed, since {}", "id is empty");
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        ColumnAction action = tcolumnService.queryActionById(vo.getId());
        return ApiResult.valueOf(action);
    }

    @PostMapping(value = "/checkFormula")
    @ResponseBody
    @ApiOperation(value = "检查公式是否合法")
    public ApiResult<TaskInstanceVO> checkFormula(@RequestBody TaskInstanceVO vo) {
        if (!vo.getData().getString("action").equals("ADD_COLUMN_BASED_ON_FORMULA")) {
            vo.setStatus("FAIL");
            vo.setLogInfo("必须是按公式新增列！");
        }
        TaskInstanceDTO taskInstanceDTO = tcolumnService.checkFormula(vo);
        TaskInstanceVO taskInstanceVO = taskInstanceDTO.view();
        return ApiResult.valueOf(taskInstanceVO);
    }

    @PostMapping(value = "/addAction")
    @ResponseBody
    @Transactional
    @ApiOperation(value = "增加action", notes = "增加action")
    public ApiResult<ColumnAction> addAction(@RequestBody @ProjectAuth TaskInstanceVO vo) {
        if (vo == null || null == vo.getTaskId() || StringUtils.isEmpty(vo.getTaskName())) {
            logger.warn("API /tcolumn/addAction failed, since {}", ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        Long userId = JwtUtil.getCurrentUserId();
        vo.setUserId(userId);
        TaskInstanceVO taskInstanceVO = tcolumnService.addAction(vo, true);
        return ApiResult.valueOf(new ColumnAction(taskInstanceVO));
    }

    @PostMapping(value = "/updateAction")
    @ResponseBody
    @Transactional
    @ApiOperation(value = "更新action", notes = "更新action")
    public ApiResult<ColumnAction> updateAction(@RequestBody @ProjectAuth TaskInstanceVO vo) {
        if (vo == null || null == vo.getTaskId() || StringUtils.isEmpty(vo.getTaskName())) {
            logger.warn("API /tcolumn/updateAction failed, since {}", ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        Long userId = JwtUtil.getCurrentUserId();
        vo.setUserId(userId);
        TaskInstanceVO taskInstanceVO = tcolumnService.updateAction(vo);
        return ApiResult.valueOf(new ColumnAction(taskInstanceVO));
    }

    @PostMapping(value = "/deleteAction")
    @ResponseBody
    @Transactional
    @ApiOperation(value = "删除action", notes = "删除action")
    public ApiResult<Boolean> deleteAction(@RequestBody @ProjectAuth TaskInstanceVO vo) {
        if (null == vo.getTaskId() || null == vo.getId()) {
            logger.warn("API /tcolumn/deleteAction failed, since {}", ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        tcolumnService.deleteActions(vo.getTaskId(), vo.getId());
        return ApiResult.valueOf(true);
    }

    @PostMapping(value = "/recommendValue")
    @ResponseBody
    @ApiOperation(value = "推荐修改值", notes = "为不匹配值推荐修改值")
    public ApiResult<JSONObject> recommendValue(@RequestBody JSONObject param) throws BadHanyuPinyinOutputFormatCombination {
        String semantic = param.getString("semantic");
        String selectValue =
                (null == param.getString("selectValue")) ? "" : param.getString("selectValue");
        if (StringUtils.isEmpty(semantic)) {
            logger.warn("API /tcolumn/recommendValue failed, since {}", ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        JSONObject result = tcolumnService.recommendValue(selectValue, semantic);
        return ApiResult.valueOf(result);
    }

    @PostMapping(value = "/queryIndex")
    @ResponseBody
    @ApiOperation(value = "查询下标", notes = "查询json/array下标")
    public ApiResult<JSONObject> queryIndex(HttpServletRequest req, @RequestBody JSONObject param) {
        if (null == param.getString("col") || null == param.getString("type") || null == param
                .getString("table")) {
            logger.warn("API /tcolumn/queryIndex failed, since {}", ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        JSONObject result = tcolumnService.queryIndex(param);
        return ApiResult.valueOf(result);
    }

    @PostMapping(value = "/queryOrder")
    @ResponseBody
    @ApiOperation(value = "查询有序类别顺序", notes = "查询有序类别顺序")
    public ApiResult<JSONArray> queryOrder(HttpServletRequest req, @RequestBody JSONObject param) {
        if (null == param.getString("taskId") || null == param.getString("col") || null == param
                .getString("table")) {
            logger.warn("API /tcolumn/queryOrder failed, since {}", ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        JSONArray ret = tcolumnService.queryOrder(param);
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/validateTransform")
    @ResponseBody
    @ApiOperation(value = "验证语义转换有效性", notes = "验证是否能进行语义转换，不能转换时返回不能转换的数据")
    public ApiResult<JSONObject> validateTransform(@RequestBody JSONObject param) {
        if (null == param.getString("col") || null == param.getString("type") || null == param
                .getString("toSemantic") || null == param.getString("table")) {
            logger.warn("API /tcolumn/validateTransform failed, since {}", ApiResultCode.PARAM_ERROR.getMessage());
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        JSONObject ret = semanticService.validateTransform(param);
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/formatList")
    @ResponseBody
    @ApiOperation(value = "格式化列表", notes = "格式化列表")
    public ApiResult<JSONObject> formatList(HttpServletRequest req, @RequestBody JSONObject param) {
        JSONObject ret = tcolumnService.formatList(param);
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/multiColumnSearch")
    @ResponseBody
    @ApiOperation(value = "多列搜索", notes = "多列搜索")
    public ApiResult<JSONObject> multiColumnSearch(@RequestBody MultiColumnQueryVO vo) {
        JSONObject ret = tcolumnService.multiColumnSearch(vo);
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/queryPercentageData")
    @ResponseBody
    @ApiOperation(value = "获取指定百分比数值所在的数据", notes = "获取指定百分比数值所在的数据")
    public ApiResult<JSONObject> queryPercentageData(@Valid @RequestBody @ProjectRoleAuth PercentageDataVO vo) {
        JSONObject obj = tcolumnService.queryPercentageData(vo);
        return ApiResult.valueOf(obj);
    }
}
